// 头文件
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <string>
#include <vector>

#define GRIDSIZE 15
#define judge_black 0
#define judge_white 1
#define grid_blank 0
#define grid_black 1
#define grid_white -1

using namespace std;

// 结构体
struct Situation {
    // 15x15
    // LineSituation[0] => x=0
    int XSituation[15]{0};
    // ColumnSituation[0]=> y=0
    int YSituation[15]{0};
    //
    int main_diag_array[19]{0};
    //
    int secd_diag_array[19]{0};
};

struct Point {
    Point() { x = y = -1; }

    Point(int _x, int _y) {
        x = _x;
        y = _y;
    }

    int x;
    int y;

    bool operator==(const Point &obj) const { return (x == obj.x && y == obj.y); }
};

struct four_int {
    int i1{0};
    int i2{0};
    int i3{0};
    int i4{0};
};

// 全局变量
int currBotColor;                        // 本方所执子颜色（1为黑，-1为白，棋盘状态亦同）
int gridInfo[GRIDSIZE][GRIDSIZE] = {0};  // 先x后y，记录棋盘状态
int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};
vector<Point> SearchSet;
// 定义各种棋型的数组
// 黑棋是1 白棋是-1 空是0 2是障碍 实际处理时候 对方的棋子和出界都看作障碍
// 只找: 活五  眠五  活四  眠四  活三 朦胧三  眠三  活二 眠二
// 下标: A5    B5    A4    B4    A3   B3      C3    A2   B2
// 后面是有对称的 后来还是发现不考虑对称太麻烦了
string BlackShapeA5[] = {"0111110", "00111101", "100111001", "10111100"};
string BlackShapeB5[] = {"20111101", "2111101", "2111110", "1101101" /*对称*/, "10111102", "1011112", "0111112",
                         "1011011"};
string BlackShapeA4[] = {"00111100"};
string BlackShapeB4[] = {"20111100", "2111100", "011011", "110011", "110101", /*对称*/ "00111102", "0011112", "110110",
                         "101011"};
string BlackShapeA3[] = {"00111000", "00110100", /*对称*/ "00011100", "00101100"};
string BlackShapeB3[] = {"20111000", "20011100", "00101010", /*对称*/ "00011102", "00111002", "01010100"};
string BlackShapeC3[] = {"200101012", "20101012", "0001112", "0011102", /*对称*/ "210101002", "21010102", "2111000",
                         "2011100"};
string BlackShapeA2[] = {"00110000", "00011000", "00101000", "00100100", /*对称*/ "00001100", "00010100"};
string BlackShapeB2[] = {"010001", "2100100", "20100100", "2110000", /*对称*/ "100010", "0010012", "00100102",
                         "0000112"};

// 白棋本来是-1但是字符串匹配不太方便, 这里用3代替, 生成每行字符串的时候要注意
string WhiteShapeA5[] = {"0333330", "00333303", "300333003", "30333300"};
string WhiteShapeB5[] = {"20333303", "2333303", "2333330", "3303303" /*对称*/, "30333302", "3033332", "0333332",
                         "3033033"};
string WhiteShapeA4[] = {"00333300"};
string WhiteShapeB4[] = {"20333300", "2333300", "033033", "330033", "330303", /*对称*/ "00333302", "0033332", "330330",
                         "303033"};
string WhiteShapeA3[] = {"00333000", "00330300", /*对称*/ "00033300", "00303300"};
string WhiteShapeB3[] = {"20333000", "20033300", "00303030", /*对称*/ "00033302", "00333002", "03030300"};
string WhiteShapeC3[] = {"200303032", "20303032", "0003332", "0033302", /*对称*/ "230303002", "23030302", "2333000",
                         "2033300"};
string WhiteShapeA2[] = {"00330000", "00033000", "00303000", "00300300", /*对称*/ "00003300", "00030300"};
string WhiteShapeB2[] = {"030003", "2300300", "20300300", "2330000", /*对称*/ "300030", "0030032", "00300302",
                         "0000332"};

// 函数

// 判断是否在棋盘内
inline bool inMap(int x, int y) {
    if (x < 0 || x >= GRIDSIZE || y < 0 || y >= GRIDSIZE) return false;
    return true;
}

// 在坐标处落子，检查是否合法或模拟落子
bool ProcStep(int x0, int y0, int x1, int y1, int grid_color, bool check_only) {
    if (x1 == -1 || y1 == -1) {
        if (!inMap(x0, y0) || gridInfo[x0][y0] != grid_blank) return false;
        if (!check_only) {
            gridInfo[x0][y0] = grid_color;
        }
        return true;
    } else {
        if ((!inMap(x0, y0)) || (!inMap(x1, y1))) return false;
        if (gridInfo[x0][y0] != grid_blank || gridInfo[x1][y1] != grid_blank) return false;
        if (!check_only) {
            gridInfo[x0][y0] = grid_color;
            gridInfo[x1][y1] = grid_color;
        }
        return true;
    }
}

int countAll(const string &basicString, const string &type) {
    unsigned long long position = 0;
    int i = 0;
    while (position < basicString.length() && (position = basicString.find(type, position)) != string::npos) {
        position += 1;
        i++;
    }
    if (i != 0) cout << "count " << type << " in " << basicString << " : " << i << '\n';
    return i;
}

string from_arrayline_to_string(int typeint, int color) {
    // 把数组的某一部分抽象成string
    // 坐标按照老师pdf的坐标建系!!!!!!!!!!!!!
    // 根据传入的颜色不同 返回字符串不尽相同 如果传入-1(白棋) 则黑棋和边界都会是2 反之亦然 两侧边界会有额外的2
    // typeint是一个返回哪一行/列/对角线位置的标识变量
    // 00~14 x=0到x=14
    // 100~114 y=0到y=14
    // 191~209 返回y=x-9到y=x+9这两条对角线上的字符串
    // 305~323 返回x+y=5到x+y=23这两条对角线上的字符串
    string returnresult;
    returnresult.reserve(17);
    // 把color从表示颜色的int转换为表示颜色的字符'1'和'3'
    char chcolor;
    if (color == grid_white) chcolor = '3';
    if (color == grid_black) chcolor = '1';
    if (typeint >= 0 && typeint <= 14) {
        // 这是返回x=typeint的字符串
        int realnumber = typeint;
        // 0和第16位是2 边缘看作障碍 1到15位存取棋盘对应行的0~14
        returnresult.push_back('2');
        for (int i = 0; i <= 14; i++) {
            if (gridInfo[realnumber][i] == 0)
                returnresult.push_back('0');
            else if (gridInfo[realnumber][i] == color)
                returnresult.push_back(chcolor);
            else
                returnresult.push_back('2');
        }
        returnresult.push_back('2');
        return returnresult;
    } else if (typeint >= 100 && typeint <= 114) {
        int realnumber = typeint - 100;
        // 0和第16位是2 边缘看作障碍 1到15位存取棋盘对应行的0~14
        returnresult.push_back('2');
        for (int i = 0; i <= 14; i++) {
            if (gridInfo[i][realnumber] == 0)
                returnresult.push_back('0');
            else if (gridInfo[i][realnumber] == color)
                returnresult.push_back(chcolor);
            else
                returnresult.push_back('2');
        }
        returnresult.push_back('2');
        return returnresult;
    } else if (typeint >= 191 && typeint <= 209) {
        // y=x-9到y=x+9
        int realnumber = typeint - 200;
        returnresult.push_back('2');
        if (realnumber <= 0 && realnumber >= -9) {
            // 这个时候以和x轴的交点作为起始点延伸
            int xbegin = -realnumber;
            int ybegin = 0;
            while (inMap(xbegin, ybegin)) {
                if (gridInfo[xbegin][ybegin] == 0)
                    returnresult.push_back('0');
                else if (gridInfo[xbegin][ybegin] == color)
                    returnresult.push_back(chcolor);
                else
                    returnresult.push_back('2');

                xbegin++;
                ybegin++;
            }
            returnresult.push_back('2');
        } else {
            // 以y轴的点作为起点延伸
            int xbegin = 0;
            int ybegin = realnumber;
            while (inMap(xbegin, ybegin)) {
                if (gridInfo[xbegin][ybegin] == 0)
                    returnresult.push_back('0');
                else if (gridInfo[xbegin][ybegin] == color)
                    returnresult.push_back(chcolor);
                else
                    returnresult.push_back('2');

                xbegin++;
                ybegin++;
            }
            returnresult.push_back('2');
        }
        return returnresult;
    } else if (typeint <= 323 && typeint >= 305) {
        // 返回x+y=5到x+y=23上面的字符串
        int realnumber = typeint - 300;
        if (realnumber >= 5 && realnumber <= 14) {
            int xbegin = realnumber;
            int ybegin = 0;
            returnresult.push_back('2');
            while (inMap(xbegin, ybegin)) {
                if (gridInfo[xbegin][ybegin] == 0)
                    returnresult.push_back('0');
                else if (gridInfo[xbegin][ybegin] == color)
                    returnresult.push_back(chcolor);
                else
                    returnresult.push_back('2');
                ybegin++;
                xbegin--;
            }
            returnresult.push_back('2');
        } else {
            // 此时是x+y=15到x+y=23
            int xbegin = realnumber - 14;
            int ybegin = 14;
            while (inMap(xbegin, ybegin)) {
                if (gridInfo[xbegin][ybegin] == 0)
                    returnresult.push_back('0');
                else if (gridInfo[xbegin][ybegin] == color)
                    returnresult.push_back(chcolor);
                else
                    returnresult.push_back('2');
                ybegin++;
                xbegin--;
            }
            returnresult.push_back('2');
        }
        return returnresult;
    } else {
        cout << "ERROR in function from_arrayline_to_string!\n";
        return {"error"};
    }
}

int GetAscore(int color, int typeint) {
    // 输入颜色和某一行的typeint, 计算这一行的分数然后返回
    string thisLineString = from_arrayline_to_string(typeint, color);
    int thisLineScore{0};

    // 这里不能简单地查找 一行可能有多个棋型 find要用自定义函数
    if (color == 1) {
        for (const auto &i: BlackShapeA5) {
            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 66666;
            }
        }
        for (const auto &i: BlackShapeB5) {
            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 66666;
            }
        }
        for (const auto &i: BlackShapeA4) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 44444;
            }

        }
        for (const auto &i: BlackShapeB4) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 44444;
            }

        }
        for (const auto &i: BlackShapeA3) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 4444;
            }


        }
        for (const auto &i: BlackShapeB3) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 4444;
            }

        }
        for (const auto &i: BlackShapeC3) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 4000;
            }

        }
        for (const auto &i: BlackShapeA2) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 400;
            }

        }
        for (const auto &i: BlackShapeB2) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 400;
            }

        }
    } else if (color == -1) {
        for (const auto &i: WhiteShapeA5) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 60000;
            }

        }
        for (const auto &i: WhiteShapeB5) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 60000;
            }

        }
        for (const auto &i: WhiteShapeA4) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 44444;
            }

        }
        for (const auto &i: WhiteShapeB4) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 44444;
            }

        }
        for (const auto &i: WhiteShapeA3) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 4444;
            }


        }
        for (const auto &i: WhiteShapeB3) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 4444;
            }

        }
        for (const auto &i: WhiteShapeC3) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 4000;
            }

        }
        for (const auto &i: WhiteShapeA2) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 400;
            }

        }
        for (const auto &i: WhiteShapeB2) {

            for (int j = 0; j < countAll(thisLineString, i); ++j) {
                thisLineScore += 400;
            }

        }
    }
    return thisLineScore;
}

four_int GetFourTypeInt(const Point &curPoint) {
    // 给定一个坐标 返回它所影响的四个typeint
    int xLine = curPoint.x;
    int yLine = curPoint.y;
    int MainLine = curPoint.y - curPoint.x;
    int SecdLine = curPoint.x + curPoint.y;
    return {xLine, 100 + yLine, 200 + MainLine, 300 + SecdLine};
}

void initiallizeSituation(int color, Situation &someoneSituation) {
    // 仅运行一次 在恢复完棋盘之后给两个situation整一下
    for (int i = 0; i <= 14; i++) {
        someoneSituation.XSituation[i] = GetAscore(color, i);
    }
    for (int i = 100; i <= 114; i++) {
        int arrayRealInt = i - 100;
        someoneSituation.YSituation[arrayRealInt] = GetAscore(color, i);
    }
    for (int i = 191; i <= 209; i++) {
        int arrayRealInt = i - 200 + 9;
        someoneSituation.main_diag_array[arrayRealInt] = GetAscore(color, i);
    }
    for (int i = 305; i <= 323; i++) {
        int arrayRealInt = i - 300 - 5;
        someoneSituation.secd_diag_array[arrayRealInt] = GetAscore(color, i);
    }
    return;
}

int fourLineSumScore(four_int x, Situation &y) {
    // 根据传入的四个typeint 和 Situation 返回对应四个直线的分数的总和
    return y.XSituation[x.i1] + y.YSituation[x.i2 - 100] + y.main_diag_array[x.i3 - 200 + 9] +
           y.secd_diag_array[x.i4 - 300 - 5];
}

void printCurrentGridInfo() {
    cout << "yx 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14" << endl;
    string Black = "⚫";
    string White = "⚪";
    for (int j = 0; j <= 14; j++) {
        if (j < 10)
            cout << "0" + to_string(j) << " ";
        else
            cout << to_string(j) << " ";
        for (int i = 0; i <= 14; i++) {
            if (gridInfo[i][j] == 0)
                cout << "00 ";
            else if (gridInfo[i][j] == -1)
                cout << White << " ";
            else
                cout << Black << " ";
        }
        cout << endl;
    }
}

int main() {
    freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false);
    int x0, y0, x1, y1;
    // 用来存储最终搜索的范围: 周围都有棋子
    // -1:这个地方已经有棋子了 0:这个地方没有棋子但是周围也没有棋子 1:这个地方没有棋子且周围有棋子
    int ToSearchSet[15][15]{0};

    // 分析自己收到的输入和自己过往的输出，并恢复棋盘状态
    int turnID;  // 即将进行的棋局数
    cin >> turnID;
    currBotColor = grid_white;  // 先假设自己是白方
    for (int i = 0; i < turnID; i++) {
        // 根据这些输入输出逐渐恢复状态到当前回合
        // 首先是对手行动
        cin >> x0 >> y0 >> x1 >> y1;
        if (x0 == -1 && y0 == -1)
            currBotColor = grid_black;  // 第一回合收到坐标是-1, -1，说明我是黑方
        else {
            ProcStep(x0, y0, x1, y1, -currBotColor, false);  // 模拟对方落子
            if (x0 == -1 || y0 == -1) {
            } else {
                ToSearchSet[x0][y0] = -1;
                for (int i = 0; i <= 7; i++) {
                    int curx = x0 + dx[i];
                    int cury = y0 + dy[i];
                    if (inMap(curx, cury) && ToSearchSet[curx][cury] == 0) ToSearchSet[curx][cury] = 1;
                }
            }

            if (x1 == -1 || y1 == -1) {
            } else {
                ToSearchSet[x1][y1] = -1;
                for (int i = 0; i <= 7; i++) {
                    int curx = x1 + dx[i];
                    int cury = y1 + dy[i];
                    if (inMap(curx, cury) && ToSearchSet[curx][cury] == 0) ToSearchSet[curx][cury] = 1;
                }
            }
        }

        // 然后是本方当时的行动
        // 对手行动总比自己行动多一个
        if (i == turnID - 1) break;  // 该你下了，不用读取了

        cin >> x0 >> y0 >> x1 >> y1;
        ProcStep(x0, y0, x1, y1, currBotColor, false);  // 模拟己方落子

        if (x0 == -1 || y0 == -1) {
        } else {
            ToSearchSet[x0][y0] = -1;
            for (int i = 0; i <= 7; i++) {
                int curx = x0 + dx[i];
                int cury = y0 + dy[i];
                if (inMap(curx, cury) && ToSearchSet[curx][cury] == 0) ToSearchSet[curx][cury] = 1;
            }
        }

        if (x1 == -1 || y1 == -1) {
        } else {
            ToSearchSet[x1][y1] = -1;
            for (int i = 0; i <= 7; i++) {
                int curx = x1 + dx[i];
                int cury = y1 + dy[i];
                if (inMap(curx, cury) && ToSearchSet[curx][cury] == 0) ToSearchSet[curx][cury] = 1;
            }
        }
    }
    // 现在确定搜索的范围
    SearchSet.reserve(100);
    for (int i = 0; i < GRIDSIZE; i++) {
        for (int j = 0; j < GRIDSIZE; j++) {
            if (ToSearchSet[i][j] == 1) SearchSet.push_back({i, j});
        }
    }
    printCurrentGridInfo();
    Situation mySituation;
    Situation enemySituation;
    initiallizeSituation(currBotColor, mySituation);
    initiallizeSituation(-currBotColor, enemySituation);
    // 最终输出的结果
    // 应该使用增量才对
    Point finalResult1 = *(SearchSet.begin());
    int highestgapscore = 0;
    for (const auto &i: SearchSet) {
        assert(gridInfo[i.x][i.y] == 0);
        four_int changeLine = GetFourTypeInt(i);
        int oldMyScore = fourLineSumScore(changeLine, mySituation);
        int oldEnemyScore = fourLineSumScore(changeLine, enemySituation);
        int oldGapScore = oldMyScore - oldEnemyScore;

        gridInfo[i.x][i.y] = currBotColor;

        int newMyScore = 0;
        newMyScore += GetAscore(currBotColor, changeLine.i1);
        newMyScore += GetAscore(currBotColor, changeLine.i2);
        newMyScore += GetAscore(currBotColor, changeLine.i3);
        newMyScore += GetAscore(currBotColor, changeLine.i4);
        int newEnemyScore = 0;
        newEnemyScore += GetAscore(-currBotColor, changeLine.i1);
        newEnemyScore += GetAscore(-currBotColor, changeLine.i2);
        newEnemyScore += GetAscore(-currBotColor, changeLine.i3);
        newEnemyScore += GetAscore(-currBotColor, changeLine.i4);
        int newGapScore = newMyScore - newEnemyScore;
        if (newGapScore - oldGapScore > highestgapscore) {
            // 这种下法带来的收益更高
            highestgapscore = newGapScore - oldGapScore;
            finalResult1 = i;
        }

        gridInfo[i.x][i.y] = 0;
    }

    gridInfo[finalResult1.x][finalResult1.y] = currBotColor;

    // 下一个子之后拓展一下搜索范围
    for (int i = 0; i <= 7; i++) {
        int curx = x0 + dx[i];
        int cury = y0 + dy[i];
        if (inMap(curx, cury) && ToSearchSet[curx][cury] == 0) SearchSet.push_back({curx, cury});
    }

    // 下一个子之后更新一下两边的分数情况
    four_int changeLine = GetFourTypeInt(finalResult1);

    // 更改我方
    mySituation.XSituation[changeLine.i1] = GetAscore(currBotColor, changeLine.i1);
    mySituation.YSituation[changeLine.i2 - 100] = GetAscore(currBotColor, changeLine.i2);
    mySituation.main_diag_array[changeLine.i3 - 200 + 9] = GetAscore(currBotColor, changeLine.i3);
    mySituation.secd_diag_array[changeLine.i4 - 300 - 5] = GetAscore(currBotColor, changeLine.i4);

    // 更改敌方
    enemySituation.XSituation[changeLine.i1] = GetAscore(currBotColor, changeLine.i1);
    enemySituation.YSituation[changeLine.i2 - 100] = GetAscore(currBotColor, changeLine.i2);
    enemySituation.main_diag_array[changeLine.i3 - 200 + 9] = GetAscore(currBotColor, changeLine.i3);
    enemySituation.secd_diag_array[changeLine.i4 - 300 - 5] = GetAscore(currBotColor, changeLine.i4);

    // 因为SearchSet没有办法移除已经下过了的子 导致有可能重子非法
    highestgapscore = 0;
    Point finalResult2 = *(SearchSet.begin());
    for (const auto &i: SearchSet) {
        // 理论上来讲只需要限制这一句
        if (i == finalResult1) continue;
        assert(gridInfo[i.x][i.y] == 0);
        four_int changeLine = GetFourTypeInt(i);
        int oldMyScore = fourLineSumScore(changeLine, mySituation);
        int oldEnemyScore = fourLineSumScore(changeLine, enemySituation);
        int oldGapScore = oldMyScore - oldEnemyScore;

        gridInfo[i.x][i.y] = currBotColor;

        int newMyScore = 0;
        newMyScore += GetAscore(currBotColor, changeLine.i1);
        newMyScore += GetAscore(currBotColor, changeLine.i2);
        newMyScore += GetAscore(currBotColor, changeLine.i3);
        newMyScore += GetAscore(currBotColor, changeLine.i4);
        int newEnemyScore = 0;
        newEnemyScore += GetAscore(-currBotColor, changeLine.i1);
        newEnemyScore += GetAscore(-currBotColor, changeLine.i2);
        newEnemyScore += GetAscore(-currBotColor, changeLine.i3);
        newEnemyScore += GetAscore(-currBotColor, changeLine.i4);
        int newGapScore = newMyScore - newEnemyScore;
        if (newGapScore - oldGapScore > highestgapscore) {
            // 这种下法带来的收益更高
            highestgapscore = newGapScore - oldGapScore;
            finalResult2 = i;
        }

        gridInfo[i.x][i.y] = 0;
    }
    cout << finalResult1.x << ' ' << finalResult1.y << ' ' << finalResult2.x << ' ' << finalResult2.y << endl;
    return 0;
}
